package org.example.config;

import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import org.example.Result;
import org.example.filter.JwtAuthenticationWebFilter;
import org.example.sentinel.IpSentinelFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
public class SecurityConfig {


    private final JwtAuthenticationWebFilter jwtAuthenticationWebFilter;


    private final IpSentinelFilter ipSentinelFilter;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }




    @Bean
    public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) throws Exception {
        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                //.cors(cors -> cors.configurationSource(corsConfigurationSource()))
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers(
                                "/api/sso/login"
                               // "/user/logout"
                        ).permitAll()
                        .anyExchange().authenticated()
                )
                .addFilterAt(jwtAuthenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .addFilterAt(ipSentinelFilter, SecurityWebFiltersOrder.FIRST)
                .exceptionHandling(handling -> handling
                                .authenticationEntryPoint(new CustomAccessDeniedHandler())
                        // .accessDeniedHandler(accessDeniedHandler)
                )
                .build();
    }


    public static class CustomAccessDeniedHandler implements ServerAuthenticationEntryPoint {
        @Override
        public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException authException) {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

            String json;
            if (authException instanceof BadCredentialsException) {
                json = JSONUtil.toJsonStr(
                        Result.error(HttpStatus.UNAUTHORIZED.value(), authException.getMessage())
                );
            } else if (authException instanceof InternalAuthenticationServiceException) {
                json = JSONUtil.toJsonStr(
                        Result.error(HttpStatus.FORBIDDEN.value(), "无权限访问该资源,请联系管理员!")
                );
            } else {
                json = JSONUtil.toJsonStr(
                        Result.error(HttpStatus.UNAUTHORIZED.value(), "匿名用户无权限访问该资源,请登录!")
                );
            }

            byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Mono.just(buffer));
        }
    }

}



